package lark

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"

	log "github.com/sirupsen/logrus"
	"prewater.com/goconfig/config"

	"strings"

	"github.com/buger/jsonparser"
)

type Lark struct {
	TokenUrl string
	GroupUrl string
	MsgUrl   string
	AppID    string
	Password string
	ChatRoom string
	Header   http.Header
}

type ContextStruct struct {
	Text string `json:"text"`
}
type SendMsgStruct struct {
	ChatID  string        `json:"chat_id"`
	MsgType string        `json:"msg_type"`
	Content ContextStruct `json:"content"`
}

func New() Lark {
	return Lark{
		TokenUrl: config.Settings.Feishu.TokenUrl,
		GroupUrl: config.Settings.Feishu.GroupUrl,
		MsgUrl:   config.Settings.Feishu.MsgUrl,
		AppID:    config.Settings.Feishu.AppId,
		Password: config.Settings.Feishu.Password,
		ChatRoom: config.Settings.Feishu.ChatRoom,
		Header:   make(http.Header, 2),
	}
}

func (l Lark) Send(msg string) error {
	chatID, err := l.getGroupID()
	if err != nil {
		return err
	}
	sendMsg := SendMsgStruct{ChatID: chatID, MsgType: "text", Content: ContextStruct{Text: msg}}
	payload, err := json.Marshal(&sendMsg)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", l.MsgUrl, bytes.NewBuffer(payload))
	if err != nil {
		return err
	}
	req.Header = l.Header

	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			log.Println(err)
		}
	}()

	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		errMsg, err := jsonparser.GetString(body, "msg")
		if err != nil {
			return err
		}
		log.Println(errMsg)
		return fmt.Errorf("lark return code: %d", resp.StatusCode)
	}

	return nil
}

func (l Lark) setHeaders() error {
	client := &http.Client{}

	payload, err := json.Marshal(map[string]string{
		"app_id":     l.AppID,
		"app_secret": l.Password,
	})
	if err != nil {
		log.Println(err)
		return err
	}
	resp, err := client.Post(l.TokenUrl, "", bytes.NewBuffer(payload))
	if err != nil {
		log.Println(err)
		return err
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			log.Println(err)
		}
	}()

	if resp.StatusCode != http.StatusOK {
		msg := fmt.Sprintf("get token return code not %d, but %d", http.StatusOK, resp.StatusCode)
		log.Println(msg)
		return errors.New(fmt.Sprintln(msg))
	}

	var arbitrary_json map[string]interface{}
	err = json.NewDecoder(resp.Body).Decode(&arbitrary_json)
	if err != nil {
		log.Println(err)
		return err
	}
	if value, ok := arbitrary_json["tenant_access_token"]; ok {
		token := value.(string)
		l.Header.Add("Content-Type", "aplication/json")
		l.Header.Add("Authorization", "Bearer "+token)
		return nil
	}
	return errors.New("no access token in returned message")
}

func (l Lark) getGroupID() (string, error) {
	err := l.setHeaders()
	if err != nil {
		return "", err
	}

	req, err := http.NewRequest("POST", l.GroupUrl, strings.NewReader(""))
	if err != nil {
		return "", err
	}
	req.Header = l.Header
	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			log.Println(err)
		}
	}()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	code, err := jsonparser.GetInt(body, "code")
	if err != nil {
		return "", fmt.Errorf("not found code in lark returned message")
	}
	if code != 0 {
		msg, err := jsonparser.GetString(body, "msg")
		if err != nil {
			return "", err
		}
		return "", fmt.Errorf(msg)
	}
	var group_id string
	_, err = jsonparser.ArrayEach(body, func(value []byte, dateType jsonparser.ValueType, offset int, err error) {
		if err == nil {
			name, err := jsonparser.GetString(value, "name")
			if err == nil {
				if name == l.ChatRoom {
					chatID, err := jsonparser.GetString(value, "chat_id")
					if err == nil {
						group_id = chatID
					}
				}
			}
		}
	}, "data", "groups")
	if err != nil {
		log.Println(err)
		return "", err
	}
	if group_id == "" {
		return "", fmt.Errorf("not find the group name")
	}
	log.Info("group_id: " + group_id)
	return group_id, nil
}

type Groups struct {
	ChatID string `json:"chat_id"`
	Name   string `json:"name"`
}
